package com.zx.sdk;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.bun.miitmdid.core.MdidSdkHelper;
import com.zx.sdk.league.member.BaseLeagueMember;
import com.zx.sdk.listener.BaseZxBannerListener;
import com.zx.sdk.listener.BaseZxRewardListener;
import com.zx.sdk.listener.BaseZxShortVideoListener;
import com.zx.sdk.listener.BaseZxSplashListener;
import com.zx.sdk.listener.ZxHostAppListener;
import com.zx.sdk.listener.ZxListener;
import com.zx.sdk.model.AdInfo;
import com.zx.sdk.model.ReadyAdPosition;
import com.zx.sdk.model.ZxError;
import com.zx.sdk.util.CollectionHelper;
import com.zx.sdk.util.ConfigHelper;
import com.zx.sdk.util.DebouncingHelper;
import com.zx.sdk.util.HttpHelper;
import com.zx.sdk.util.JsonHelper;
import com.zx.sdk.util.LeagueMemberHelper;
import com.zx.sdk.util.LogHelper;
import com.zx.sdk.util.RunnableHelper;
import com.zx.sdk.util.ZxActionReporter;
import com.zx.sdk.util.ZxEventHelper;
import com.zx.sdk.view.widget.BaseNewsAdView;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
 * ZxSDK主程序
 */
public class ZxSDK {

    public static final int Min_Bid_Cpm = 500;

    public static final String FETCH_ONLY = "FETCH_ONLY";
    public static final String FETCH_AND_SHOW = "FETCH_AND_SHOW";

    /**
     * 展示类型
     */
    public static final String SPLASH = "splash";
    public static final String REWARD = "reward";
    public static final String BANNER = "banner";
    public static final String INTERSTITIAL = "Illustration";
    public static final String SUSPENSION = "Suspension";

    /**
     * 竞价类型
     */
    public static final String Ground = "ground";
    public static final String Target = "target";
    public static final String Bidding = "bidding";

    private static Context context;
    private static String appId; // Zx平台appId
    private static int appVersion; // Zx平台appId
    private static String wxAppId; // Zx平台appId
    private static String appName; // app名称
    public static String oaid; // Zx平台oaid
    public static String aaid; // Zx平台aaid
    public static String vaid; // Zx平台vaid
    private static boolean debug;
    public static ZxHostAppListener listener;
    public static WeakReference<Activity> topActivityRef;
    public static boolean preloading = false;//splash和reward不会同时预加载共用一个标志，暂时用bool以后业务状态多了再用enum
    public static String BASE_API_URL = "https://ssp-api.yctourism.com";

    public static void init(Context applicationContext, String mobAppId, int appVersion, String appName, String apiUrl, boolean debug, String wxAppId, ZxHostAppListener listener) {
        context = applicationContext;
        LogHelper.init((Application) context.getApplicationContext());
        ZxSDK.appId = mobAppId;
        ZxSDK.appVersion = appVersion;
        ZxSDK.appName = appName;
        ZxSDK.debug = debug;
        ZxSDK.listener = listener;
        ZxSDK.wxAppId = wxAppId;
        ZxSDK.BASE_API_URL = apiUrl;
        LogHelper.d("开始初始化 appId = " + mobAppId + " appName = " + appName + " debug = " + debug);
        ConfigHelper.loadConfig(false);
        ZxEventHelper.setZxEventHandler(ZxSDK.listener.getZxEventHandler());
        ZxActionReporter.INSTANCE.start();
        MdidSdkHelper.InitSdk(applicationContext, true, (b, idSupplier) -> {
            if (idSupplier == null) {
                return;
            }
            ZxSDK.oaid = idSupplier.getOAID();
            ZxSDK.aaid = idSupplier.getAAID();
            ZxSDK.vaid = idSupplier.getVAID();
            LogHelper.d("oaid ", ZxSDK.oaid, "aaid", ZxSDK.aaid, "vaid", ZxSDK.vaid);
        });
    }

    public static Context getContext() {
        if (context == null) {
            LogHelper.d("SDK尚未初始化");
        }
        return context;
    }

    public static String getAppId() {
        return appId;
    }

    public static int getAppVersion() {
        return appVersion;
    }

    public static String getAppName() {
        return appName;
    }

    public static boolean isDebug() {
        return debug;
    }

    public static String getUid() {
        return listener.getUserId();
    }

    public static String getWxAppId() {
        return wxAppId;
    }

    /**
     * 预加载广告
     */
    public static void preloadAdv(Activity context, String type, String mobPid, ViewGroup container, ZxListener listener) {
        if (preloading) {
            LogHelper.d("上一次预加载还未完成 " + type + " 广告 pid = " + mobPid);
            return;
        }
        LogHelper.d("开始预加载 " + type + " 广告 pid = " + mobPid);

        // 预加载bidding
        ArrayList<AdInfo> biddings = ConfigHelper.getBidding(mobPid);
        if (CollectionHelper.isNoEmpty(biddings)) {
            for (AdInfo adInfo : biddings) {
                BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(adInfo.getLeague());
                switch (type) {
                    case SPLASH:
                        member.loadSplash(context, adInfo, container, FETCH_ONLY, adInfo.getBillType(), (BaseZxSplashListener) listener);
                        break;
                    case REWARD:
                        member.loadReward(context, adInfo, FETCH_ONLY, adInfo.getBillType(), (BaseZxRewardListener) listener);
                        break;
                    case INTERSTITIAL:
                        member.loadInterstitial(context, adInfo, FETCH_ONLY, adInfo.getBillType(), (BaseZxRewardListener) listener);
                        break;
                    case BANNER:
                        member.loadBanner(context, adInfo, FETCH_ONLY, container, adInfo.getBillType(), (BaseZxBannerListener) listener);
                        break;
                    case SUSPENSION:
                        member.loadSuspension(context, adInfo, FETCH_ONLY, container, adInfo.getBillType(), (BaseZxBannerListener) listener);
                        break;
                }
            }
        }

        // 开线程预加载
        new Thread(() -> {
            try {
                ArrayList<AdInfo> targets = ConfigHelper.getTargets(mobPid);
                ArrayList<AdInfo> preloadAdvs = new ArrayList<>();
                if (CollectionHelper.isNoEmpty(targets)) {
                    preloadAdvs.addAll(targets);
                }

                preloading = true;
                RunnableHelper.runOnUIThread(listener::onPreloading);
                innerPreloadAdvs(preloadAdvs, context, type, mobPid, container, listener);
            } catch (InterruptedException e) {
                LogHelper.e("preloadAdv", e);
            }
            preloading = false;
            RunnableHelper.runOnUIThread(listener::onLoaded);
        }).start();
    }

    /**
     * 内部预加载广告规则执行
     */
    private static void innerPreloadAdvs(@NonNull ArrayList<AdInfo> preloadAdvs, Activity context, String type, String mobPid, ViewGroup container, ZxListener listener) throws InterruptedException {
        // 如果预加载失败，则记录这个广告位的加载失败时间
        listener.setOnPreloadErrorListener((error, adInfo) -> {
            LogHelper.d("预加载" + type + " 广告失败，记录失败时间 pid = " + adInfo.getMapPid());
            StrategyHelper.onFillError(adInfo.getMapAppid(), adInfo.getMapPid());
        });

        StrategyHelper.printState(preloadAdvs);

        int clusterSizeCounter = 0;
        for (int i = 0; i < preloadAdvs.size(); i++) {
            AdInfo adInfo = preloadAdvs.get(i);
            BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(adInfo.getLeague());
            String mapPid = adInfo.getMapPid();
            // 检查广告位是否可以开始加载，不行则下一个,只让能加载的进簇。
            if (StrategyHelper.isFilterPreload(type, adInfo, member)) {
                continue;
            }
            LogHelper.d("预加载当前簇" + type + " 广告 clusterSizeCounter = " + clusterSizeCounter, "pid = " + mapPid);
            switch (type) {
                case SPLASH:
                    member.loadSplash(context, adInfo, container, FETCH_ONLY, adInfo.getBillType(), (BaseZxSplashListener) listener);
                    break;
                case REWARD:
                    member.loadReward(context, adInfo, FETCH_ONLY, adInfo.getBillType(), (BaseZxRewardListener) listener);
                    break;
                case INTERSTITIAL:
                    member.loadInterstitial(context, adInfo, FETCH_ONLY, adInfo.getBillType(), (BaseZxRewardListener) listener);
                    break;
                case BANNER:
                    member.loadBanner(context, adInfo, FETCH_ONLY, container, adInfo.getBillType(), (BaseZxBannerListener) listener);
                    break;
                case SUSPENSION:
                    member.loadSuspension(context, adInfo, FETCH_ONLY, container, adInfo.getBillType(), (BaseZxBannerListener) listener);
                    break;
            }
            clusterSizeCounter += 1;
            // 当前簇达到分簇边界或者最后一个加载完, 则 clusterInterval 后再检查结果。
            if (clusterSizeCounter == ConfigHelper.getClusterCount() || i == preloadAdvs.size() - 1) {
                LogHelper.d("广告预加载当前簇已经加载完成", type, "广告 pid = " + mobPid);
                // 如果当前簇中有正在加载中的广告位则【sleep 约定秒】，否则不等待直接加载下一簇
                int delayTime = ConfigHelper.getClusterInterval();
                LogHelper.d("睡眠 " + delayTime + " 秒,等待该簇加载", type, "广告 pid = " + mobPid);
                Thread.sleep(delayTime);
                if (isPreloadAdvEnough(preloadAdvs, i, type)) {
                    LogHelper.d("预加载填充数量足够，不再继续加载 ", type, "广告 pid = " + mobPid);
                    return;
                }
                clusterSizeCounter = 0;
                LogHelper.d("预加载填充数量不足，开始预加载下一簇 ", type, "广告 pid = " + mobPid);
            }
        }
    }

    /**
     * 检查预加载的广告够不够用
     *
     * @param end 当前位置之前的缓存广告够不够，而不是所有的广告够不够，防止低价广告被缓存，高价在第二簇或以后的话就没机会加载了。
     */
    private static boolean isPreloadAdvEnough(ArrayList<AdInfo> preloadAdvs, int end, String type) {
        int count = 0;
        for (int i = 0; i <= end; i++) {
            AdInfo preloadAdv = preloadAdvs.get(i);
//            LogHelper.d("检查是否有足够的预加载广告 pid =" + preloadAdv.getMapPid());
            if (preloadAdv.isBiddingPosition()) {
                LogHelper.d("检查是否有足够的预加载广告 pid =" + preloadAdv.getMapPid() + " 为bidding广告 不计入填充数量count = " + count);
                continue;// bidding参与最后的竞价，所以不计入填充数量
            }
            BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(preloadAdv.getLeague());
            if (member != null && member.isPreResReady(type, preloadAdv.getMapPid())) {
                count += 1;
                LogHelper.d("检查是否有足够的预加载广告 pid =" + preloadAdv.getMapPid() + " 计入填充数量，当前数量count = " + count);
            }
        }
        return count >= 2;
    }

    /**
     * 显示 Reward广告
     */
    public static void showTargetAndBidding(Activity context, String mobPid, String type, ViewGroup container, ZxListener listener) {
        if (!DebouncingHelper.isValid("showTargetAndBidding")) {
            return;
        }
        // 封装 排序好的list
        List<ReadyAdPosition> waterFull = new ArrayList<>();
        ArrayList<AdInfo> targets = ConfigHelper.getTargets(mobPid);
        ArrayList<AdInfo> biddingList = ConfigHelper.getBidding(mobPid);

        ArrayList<AdInfo> allPosition = new ArrayList<>();
        if (CollectionHelper.isNoEmpty(targets)) {
            allPosition.addAll(targets);
        }
        if (CollectionHelper.isNoEmpty(biddingList)) {
            allPosition.addAll(biddingList);
        }

        if (CollectionHelper.isEmpty(allPosition)) {
            showTarget(context, mobPid, Collections.emptyList(), type, container, 0, listener);
            return;
        }

        // 对准备好的 target和bidding广告位 做排序，准备曝光
        for (AdInfo item : allPosition) {
            BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(item.getLeague());
            if (!member.isPreResReady(type, item.getMapPid())) {
                LogHelper.d("联盟广告位未就绪", item.getLeague(), item.getMapPid(), String.valueOf(!member.isPreResReady(type, item.getMapPid())));
                continue;
            }

            // 非 bidding 位 使用后台返回的cpm
            int cpm = item.getCpm() * 100;
            // bidding的目标价是不确定的，需要加载成功后才知道
            if (item.isBiddingPosition()) {
                cpm = member.getCpmByPid(type, item.getMapPid());
                // 允许的最低 cpm，低于这个值 bidding不展示
                int minBidCpm = ConfigHelper.getMinBidCpm(mobPid);
                if (cpm < minBidCpm) {
                    LogHelper.d("少于 minBidCpm 已被过滤 ", item.getLeague(), item.getDisplay(), item.getAdType() + "", item.getMapPid(), "cpm=" + cpm, "minBidCpm = " + minBidCpm);
                    continue;
                }
            }

            // 加入 ready 瀑布流
            waterFull.add(ReadyAdPosition.of(item, cpm));
        }

        // 对价格排序
        Collections.sort(waterFull);
        // 回调竞价结果
        notifyBidding(mobPid, type, waterFull, biddingList);
        showTarget(context, mobPid, waterFull, type, container, 0, listener);
    }

    /**
     * 回调竞价结果
     *
     * @param mobPid      zxPid
     * @param type        广告类型 - 开屏、激励视频
     * @param waterFull   广告瀑布流
     * @param biddingList 竞价广告位列表
     */
    private static void notifyBidding(String mobPid, String type, List<ReadyAdPosition> waterFull, ArrayList<AdInfo> biddingList) {
        LogHelper.d("开始竞价回调", " 竞争者数量 " + waterFull.size());
        // 找到竞胜者 没有则为底价
        AdInfo minBidCpm = new AdInfo();
        ReadyAdPosition winner = ReadyAdPosition.of(minBidCpm, ConfigHelper.getMinBidCpm(mobPid));
        if (!waterFull.isEmpty()) {// 存在则为第一个
            winner = waterFull.get(0);
        }
        // 找到竞败者
        ReadyAdPosition loser = null;
        if (waterFull.size() >= 2) {
            loser = waterFull.get(1);
        }
        // 通知bidding回调
        for (AdInfo item : biddingList) {
            BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(item.getLeague());
            // 遍历对象是否是竞胜者 条件：存在竞胜者，appId和pid都相等
            boolean isWinner = winner != null
                    && item.getMapAppid().equalsIgnoreCase(winner.getAdInfo().getMapAppid())
                    && item.getMapPid().equalsIgnoreCase(winner.getMapPid());
            try {
                // 回调竞价结果
                if (isWinner) {
                    member.notifyBiddingWin(type, item.getMapPid(), loser);
                } else {
                    member.notifyBiddingLose(type, item.getMapPid(), winner, LoseReason.LOW_PRICE);
                }
            } catch (Throwable e) {
                LogHelper.e("notifyBidding", e);
            }
        }
    }

    /**
     * 显示目标广告
     */
    private static void showTarget(Activity context, String mobPid, List<ReadyAdPosition> targets, String type, ViewGroup container, int level, ZxListener listener) {
        LogHelper.d("开始显示目标" + type + "广告 level = " + level + " pid = " + mobPid);
        if (targets == null || level >= targets.size()) {
            LogHelper.d("显示目标" + type + "广告 目标广告脱靶，显示兜底广告 pid = " + mobPid);
            showGround(context, ConfigHelper.getGrounds(mobPid), mobPid, type, container, listener);
            return;
        }
        ReadyAdPosition target = targets.get(level);
        BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(target.getLeague());
        if (member == null) {
            LogHelper.e("没有找到pid对应的目标" + type + "广告联盟成员 pid = " + mobPid + " league = " + target.getLeague());
        } else {
            listener.setOnShowErrorListener((error, adInfo) -> {
                showTarget(context, mobPid, targets, type, container, level + 1, listener);
            });
            switch (type) {
                case SPLASH:
                    if (member.showSplash(target.getAdInfo(), container, (BaseZxSplashListener) listener)) {
                        StrategyHelper.saveLastWinPrice(type, target.getCpm(), target.getMapPid());
                        LogHelper.d("显示目标" + type + "广告成功命中 pid = " + mobPid);
                        uploadHitInformation(target.getAdInfo().getMapAppid(), target.getMapPid(), target.getCpm());
                        return;
                    }
                    break;
                case REWARD:
                    if (member.showReward(context, target.getAdInfo(), (BaseZxRewardListener) listener)) {
                        StrategyHelper.saveLastWinPrice(type, target.getCpm(), target.getMapPid());
                        LogHelper.d("显示目标" + type + "广告成功命中 pid = " + mobPid);
                        return;
                    }
                    break;
                case INTERSTITIAL:
                    if (member.showInterstitial(context, target.getAdInfo(), (BaseZxRewardListener) listener)) {
                        StrategyHelper.saveLastWinPrice(type, target.getCpm(), target.getMapPid());
                        LogHelper.d("显示目标" + type + "广告成功命中 pid = " + mobPid);
                        uploadHitInformation(target.getAdInfo().getMapAppid(), target.getMapPid(), target.getCpm());
                        return;
                    }
                case BANNER:
                    if (member.showBanner(context, target.getAdInfo(), container, (BaseZxBannerListener) listener)) {
                        StrategyHelper.saveLastWinPrice(type, target.getCpm(), target.getMapPid());
                        LogHelper.d("显示目标" + type + "广告成功命中 pid = " + mobPid);
                        uploadHitInformation(target.getAdInfo().getMapAppid(), target.getMapPid(), target.getCpm());
                        return;
                    }
                    break;
                case SUSPENSION:
                    if (member.showSuspension(context, target.getAdInfo(), container, (BaseZxBannerListener) listener)) {
                        StrategyHelper.saveLastWinPrice(type, target.getCpm(), target.getMapPid());
                        LogHelper.d("显示目标" + type + "广告成功命中 pid = " + mobPid);
                        uploadHitInformation(target.getAdInfo().getMapAppid(), target.getMapPid(), target.getCpm());
                        return;
                    }
                    break;
            }
        }
        LogHelper.d("显示目标" + type + "广告 目标广告脱靶 level = " + level + " pid = " + mobPid);
        showTarget(context, mobPid, targets, type, container, level + 1, listener);
    }

    private static void uploadHitInformation(String mapAppid, String mapPid, int cpm) {
        uploadHitInformation(mapAppid, mapPid, cpm, 0);
    }

    //上传广告命中信息
    public static void uploadHitInformation(String mapAppId, String mapPid, int cpm, int requestType) {

        LogHelper.d("广告命中推送***********************- " + mapAppId + " -- " + mapPid + " -- " + cpm);
        String secret;

        TreeMap<String, String> params = new TreeMap<>();
        Map<String, String> headers = new HashMap<>();
        if (context.getPackageName().startsWith("com.yicong.ants")) {
            //蚁从
            secret = ZxSDK.isDebug() ? "Y8u9yGe1QGv0CZA1" : "QEhwvCVZ$j!UuXtt";
            params.put("dev_token", ZxSDK.listener.getToken());
        } else if (context.getPackageName().startsWith("com.chain.tourist")) {
            //安卓联盟链及联盟链子项目
            secret = ZxSDK.isDebug() ? "uD0seU02A2xZ1adk" : "L7K3yMv=!ThTywXy";
            params.put("dev_token", ZxSDK.listener.getToken());
        } else if (context.getPackageName().startsWith("com.antybt")) {
            //游吧通
            secret = ZxSDK.isDebug() ? "3JWH&5xY2DJcWgXW" : "WsLUhpJh%2QBnt#6";
            params.put("deviceId", ZxSDK.listener.getDeviceId());
        } else {
            return;
        }

        try {
            if (requestType > 0) {
                params.put("requestType", requestType + "");
            }
            params.put("appId", ZxSDK.getAppId());
            params.put("mapAppid", mapAppId);//广告位的mapAppid
            params.put("mapPid", mapPid);//广告位的mapPid
            params.put("cpm", String.valueOf((cpm * 0.01)));//三方sdk返回的价格
            params.put("token", ZxSDK.listener.getUserToken());

            params.put("device_platform", "android");
            params.put("device_type", Build.MODEL);
            params.put("device_brand", Build.BRAND);
            params.put("os_version", Build.VERSION.RELEASE);
            params.put("version_code", String.valueOf(ZxSDK.getAppVersion()));

            SingUtils singUtils = new SingUtils();

            String noncestr = singUtils.generatingRandomString();
            headers.put("noncestr", noncestr);
            String time = String.valueOf(System.currentTimeMillis());
            headers.put("timestamp", time);

            params.put("signature", singUtils.sign(secret, params, noncestr, time));//加密字符串
        } catch (Exception e) {
            pusAdLogs(e.getMessage());
            return;
        }

        HttpHelper.retryPostJSON(ZxSDK.BASE_API_URL + "/api/client/exposureStatistics", headers, JsonHelper.toJson(params), new Callback() {

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) {
                params.put("success", "");
                pusAdLogs(JsonHelper.toJson(params));
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                LogHelper.d("广告命中推送异常：" + e.getMessage());
                params.put("error", e.getMessage());
                pusAdLogs(JsonHelper.toJson(params));
            }
        });
    }

    private static void pusAdLogs(String json) {
        if (!ConfigHelper.isLogPushSwitch()) return;
        var releaseUrl = "https://api.youbatong.com/";
        if (ZxSDK.isDebug()) {
            releaseUrl = "http://api-test.youbatong.com/";
        }
        LogHelper.d("广告命中推送二级日志Start：" + releaseUrl + "/api/v2/client/clientLog" + "  ----  " + json);
        HttpHelper.postJSON(releaseUrl + "/api/v2/client/clientLog", json, new Callback() {

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                LogHelper.d("广告命中推送二级日志：" + response.code() + "  ------   ");
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                LogHelper.d("广告命中推送异常二级日志：" + e.getMessage());
            }
        });
    }

    /**
     * 显示兜底广告
     */
    private static void showGround(Activity context, ArrayList<AdInfo> grounds, String mobPid, String type, ViewGroup container, ZxListener listener) {
        LogHelper.d("开始显示兜底" + type + "广告 pid = " + mobPid);
        if (CollectionHelper.isEmpty(grounds)) {
            String msg = "没有找到pid对应的兜底" + type + "广告 pid = " + mobPid;
            LogHelper.e(msg);
            if (ZxSDK.INTERSTITIAL.equals(type)) {
                listener.onNoAD(new ZxError(ZxError.NO_AD_FINAL_INTERSTITIAL, msg));
            } else {
                listener.onNoAD(new ZxError(ZxError.NO_AD_FINAL, msg));
            }
            return;
        }
        AdInfo ground = getRandomGround(grounds);// 通过计算weight得到兜底广告
        if (ground == null) {
            String msg = "没有找到pid对应的兜底" + type + "广告 pid = " + mobPid;
            LogHelper.e(msg);
            if (ZxSDK.INTERSTITIAL.equals(type)) {
                listener.onNoAD(new ZxError(ZxError.NO_AD_FINAL_INTERSTITIAL, msg));
            } else {
                listener.onNoAD(new ZxError(ZxError.NO_AD_FINAL, msg));
            }
            return;
        }
        BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(ground.getLeague());
        if (member == null) {
            String msg = "没有找到pid对应的兜底" + type + "广告联盟成员 pid = " + mobPid + " league = " + ground.getLeague();
            LogHelper.e(msg);
            listener.onNoAD(new ZxError(ZxError.NO_LEAGUE_MEMBER, msg));
            return;
        }
        listener.setOnADExposeListener(() -> {
            if (!type.equals(REWARD)) {
                uploadHitInformation(ground.getMapAppid(), ground.getMapPid(), ground.getCpm());
            }
            StrategyHelper.saveLastWinPrice(type, ConfigHelper.getMinBidCpm(mobPid), ground.getMapPid());
        });
        listener.setOnShowErrorListener((error, adInfo) -> {
            ArrayList<AdInfo> adInfos = (ArrayList<AdInfo>) grounds.clone();
            adInfos.remove(ground); // 移除显示失败的兜底广告，然后继续加载其他兜底的广告
            showGround(context, adInfos, mobPid, type, container, listener);
        });
        switch (type) {
            case SPLASH:
                if (member.isSplashContainerNotVisible(container)) {
                    String msg = "pid对应的兜底" + type + "加载失败，广告联盟成员 pid = " + mobPid + " league = " + ground.getLeague();
                    LogHelper.e(msg);
                    listener.onNoAD(new ZxError(ZxError.SPLASH_CONTAINER_NOT_VISIBLE, msg));
                    return;
                }

                member.loadSplash(context, ground, container, FETCH_AND_SHOW, ground.getBillType(), (BaseZxSplashListener) listener);
                break;
            case REWARD:
                member.loadReward(context, ground, FETCH_AND_SHOW, ground.getBillType(), (BaseZxRewardListener) listener);
                break;
            case INTERSTITIAL:
                member.loadInterstitial(context, ground, FETCH_AND_SHOW, ground.getBillType(), (BaseZxRewardListener) listener);
                break;
            case BANNER:
                member.loadBanner(context, ground, FETCH_AND_SHOW, container, ground.getBillType(), (BaseZxBannerListener) listener);
                break;
            case SUSPENSION:
                member.loadSuspension(context, ground, FETCH_AND_SHOW, container, ground.getBillType(), (BaseZxBannerListener) listener);
                break;
        }
    }

    /**
     * 通过计算weight得到兜底广告
     */
    private static AdInfo getRandomGround(@NonNull ArrayList<AdInfo> adInfos) {
        if (CollectionHelper.isEmpty(adInfos)) {
            return null;
        }
        // 构建随机池
        ArrayList<AdInfo> adInfoPool = new ArrayList<>();
        // 填充随机池
        for (AdInfo adInfo : adInfos) {
            for (int i = 0; i < adInfo.getWeight(); i++) {
                adInfoPool.add(adInfo);
            }
        }
        if (CollectionHelper.isEmpty(adInfoPool)) {
            return null;
        }
        // 获取随机数
        int index = new Random().nextInt(adInfoPool.size());
        return adInfoPool.get(index);
    }

    /**
     * 获取短视频
     */
    public static Fragment getShortVideoFragment(String pid, BaseZxShortVideoListener listener) {
        LogHelper.d("开始获取短视频Fragment pid = " + pid);
        ArrayList<AdInfo> adInfos = ConfigHelper.getGrounds(pid);
        if (CollectionHelper.isEmpty(adInfos)) {
            String msg = "没有找到pid对应的短视频Fragment pid = " + pid;
            LogHelper.e(msg);
            return null;
        }
        AdInfo ground = getRandomGround(adInfos);
        if (ground == null) {
            String msg = "没有找到pid对应的短视频Fragment pid = " + pid;
            LogHelper.e(msg);
            return null;
        }
        BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(ground.getLeague());
        if (member == null) {
            LogHelper.e("没有找到pid对应的短视频广告联盟成员 pid = " + pid + " league = " + ground.getLeague());
            return null;
        }
        return member.getShortVideoFragment(listener, ground);
    }

    /**
     * 清空已填充的bidding
     *
     * @param type type
     */
    public static void notifyClearBidding(String type, String leaguePid) {
        String mobPid = ConfigHelper.getMobPidByLeaguePid(leaguePid);
        LogHelper.d(" notifyClearBidding " + type + " " + mobPid);
        ArrayList<AdInfo> bidding = ConfigHelper.getBidding(mobPid);
        if (CollectionHelper.isEmpty(bidding)) {
            return;
        }

        for (AdInfo item : bidding) {
            BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(item.getLeague());
            if (member == null) {
                continue;
            }
            member.clearFilledBiddingAd(type, item.getMapPid());
        }
    }

    public static void destroyBanner(Activity activity, String pid) {
        LeagueMemberHelper.foreach(member -> {
            member.destroyBanner(activity, pid);
        });
    }

    public static void setTopActivity(Activity activity) {
        topActivityRef = new WeakReference<>(activity);
    }

    public static BaseNewsAdView getNewsView(Context context, String pid) {
        LogHelper.d("开始获取新闻资讯", "pid", pid);
        ArrayList<AdInfo> adInfos = ConfigHelper.getGrounds(pid);
        if (CollectionHelper.isEmpty(adInfos)) {
            String msg = "没有找到pid对应的新闻资讯 pid = " + pid;
            LogHelper.e(msg);
            return null;
        }
        AdInfo ground = getRandomGround(adInfos);
        if (ground == null) {
            String msg = "没有找到pid对应的新闻资讯 pid = " + pid;
            LogHelper.e(msg);
            return null;
        }
        BaseLeagueMember<?, ?, ?, ?, ?> member = LeagueMemberHelper.getMember(ground.getLeague());
        if (member == null) {
            LogHelper.e("没有找到pid对应的新闻资讯广告联盟成员 pid = " + pid + " league = " + ground.getLeague());
            return null;
        }
        return member.getNewsView(context, ground);
    }

    public static final HashMap<String, Object> shownSplash = new HashMap<>();
    public static final HashMap<String, Object> shownReward = new HashMap<>();

    public static void destroySplash(@NotNull Activity activity, Boolean onlyShown) {
        for (String key : shownSplash.keySet()) {
            try {
                BaseLeagueMember baseLeagueMember = LeagueMemberHelper.getMember(key);
                baseLeagueMember.destroyShownSplash(activity, shownSplash.get(key));
            } catch (Throwable ignore) {
            }
        }
        shownSplash.clear();
        if (onlyShown) return;
        LeagueMemberHelper.foreach(member -> {
            member.destroySplash(activity);
        });
    }

    public static void destroyReward(@NotNull Activity activity, Boolean onlyShown) {
        for (String key : shownReward.keySet()) {
            try {
                BaseLeagueMember baseLeagueMember = LeagueMemberHelper.getMember(key);
                baseLeagueMember.destroyShownReward(activity, shownReward.get(key));
            } catch (Throwable ignore) {
            }
        }
        shownReward.clear();
        if (onlyShown) return;
        LeagueMemberHelper.foreach(member -> {
            member.destroyReward(activity);
        });
    }

    public static String getToken() {
        return listener.getUserToken();
    }

    public enum LoseReason {
        LOW_PRICE, NO_AD
    }
}
